Decoding Nikon NEF Files
NEF is the file format Nikon cameras use to store RAW pictures. There is no actionable documentation on the format. This page collects what I have been piecing together from various sources -- usually incomplete blog posts or very vague forum posts -- and by reading the source codes of dcraw↗ and libopenraw↗.
All of this was verified on images taken with my Nikon D40 and on sample NEF images I have found online↗, but I do not guarantee correctness. In fact, I am aware that there are additional NEF variants that are not covered here.
If you know any additional information that is not listed here, please reach out!
I hope to do more interesting and creative adventures in image manipulation in the future, however this article is fairly dry. Unless you are interested in parsing NEF, you might not get much out of it.
You have been warned!
Introduction
While it pretends to be a fancy special file format, in reality NEF files are mostly just TIFF with some custom tags. If you want to decode NEF, you should read up o TIFF first; You should know what IFDs and sub-IFDs are and how to access them, what dir-entry tags and types are and how to read them, how IFDs contain images, and so on. This article henceforth assumes the reader is familiar with TIFF.
The custom TIFF tags Nikon uses in NEF are known↗. Thanks to the maintainers of EXIF Tool for this wonderfully compiled list, which was immensely helpful.
General Structure
NEF files are TIFF files. They start with the normal TIFF magic, indicating byte order. NEF files contain one main IFD, which seems to always be at offset 8 -- but you should not hardcode that, just read the location of the main IFD the way TIFF dictates.
Note that you can not reliably know what information an IFD holds without parsing it. I recommend a parsing strategy that works with a stack of IFD offsets, parsing them in sequence, adding new offsets to the stack as needed, while guessing the IFD type based on the available tags.
Main IFD
The main IFD contains an uncompressed RGB thumbnail of the image.
The following direntries are of interest:
0x14A"SubIfd": Holds the offsets of at least one sub-IFD which contains the raw image data.Some cameras also have an additional sub-IFD which contains a JPEG version of the image. I am not very interested in that, so it is not described here any further.
You do not know which of these two IFDs holds what without parsing them.
I have not encountered a NEF file which points to more than these two sub-IFDs in this field.
0x8769"EXIF": Offset to the IFD containing EXIF metadata.0x110"model": The camera model name, as a human readable string.
EXIF IFD
You can get a lot of neat metadata from the EXIF IFD. The EXIF TIFF tags are well documented.
The following direntries are of special interest here:
0x927c"MakerNote": Offset of the so-called makernote, which is a data structure containing more metadata as well as the offsets to the compression curve and the contrast curve.The makernote is not an IFD!
0x8827"ISO": The ISO of the image. Probably useful to tune some post-processing filters, perhaps.
MakerNote
Rather than adding another (sub-)IFD as the makernote, Nikon instead decided to embed another full TIFF document.
At the makernote offset, you should find the null-terminated magic string "Nikon". After that come 32 bits which I suspect to hold a version tag, but so far I did not find any use to parsing that.
Afterwards, you should find another TIFF magic string. Careful: Since the endianness of a TIFF document depends on the magic ("MM" vs "II"), it is theoretically possible that this sub-TIFF has a different one than the outer TIFF, however I have not yet encountered such cursed files.
The makernote should be the main IFD of the sub-TIFF. All offsets inside it are relative to the sub-TIFF instead of global file offsets.
There might be a different makernote format for older Nikon cameras, however I have not yet looked at that.
Main IFD of MakerNote
The actual contents of the makernote can be accessed via its main IFD. All offsets read from here are relative to the sub-TIFF instead of global file offsets.
The following direntries are of interest:
0x96"LinearisationTable": Offset of the linearisation table.0x8c"ContrastCurve": Offset of the contrast curveNone of the FOSS NEF decoders I have looked at use the contrast curves. They do not seem to be strictly necessary for decoding, however I hope to eventually find out how to use them.
dcrawtechnically has a code path using them: When no linearisation table offset is found, the contrast curve offset is used as a fallback. I am reasonably sure that this will never happen, as all NEF files seem to have an embedded linearisation table. I am also pretty sure this would causedcrawto segfault, as the contrast curve seems to have a different format than the linearisation table.
0x93"NEF Compression": The compression type of the pixel datapub const NEFCompression = enum(u16) { lossy_type_1 = 1, uncompressed = 2, lossless = 3, lossy_type_2 = 4, striped_packed_12bit = 5, uncompressed_reduced_12bit = 6, unpacked_12_bit = 7, small = 8, packed_12bit = 9, packed_14bit = 10, high_efficiency = 13, high_efficiency_star = 14, };I am unsure whether this value is needed. When parsing the linearisation table the compression type can be inferred from its version tag.
This might be for different cameras than the ones I tested?
Linearisation Table
At the linearisation table offset, you can read two unsigned bytes,
henceforth referred to as version0 and version1.
According to dcraw, if version0 equals 0x49 you need to seek forward
by 2110 now. I do not know why. Perhaps there is additional data we
skip, but its purpose is unknown to me.
Now you can read four 16 bit unsigned integers, the vpred table, and
another 16 bit unsigned integer, the curve data count.
There are multiple different curve types with different decoding strategies.
full:
version0is0x46.dcrawalso checks if the curve data count is at most0x4001. This is likely a sanity check, but since barely any of its source code is documented we can only guess.
sparse:
version0is0x44andversion1is0x20.
After decoding the curve, it may have a "tail" of repeating values, which needs to be pruned.
I am somewhat sure other curve types exist, however I have not encountered them yet. I suspect, based on some vague sources, that NEF variants that work completely differently also exist. I know that some NEF variants need a white balance table, which may be encrypted↗, however I have yet to properly look into those.
Decoding full curves
This curve type needs no decoding. Simply read as many unsigned 16 bit integers as the curve data count.
Decoding sparse curves
This curve type is not stored in the file in its complete form. It has gaps we need to fill in.
Calculate the maximum amount of elements in the curve using the sample
depth (henceforth bps "bits per sample").
debug.assert(12 == bps or 14 == bps);
const max = 1 << bps;
Calculate the step size.
const step = max / (curve_data_count - 1);
debug.assert(0 != step);
Read as many unsigned 16 bit integers as indicated by the curve data count, spaced out by the step.
for (0..curve_data_count) |i|
curve[i * step] = takeInt(u16);
To fill in the elements in between the values you have just read from the file, calculate the average of the two nearest values from the file, weighted by index distance.
for (0..max) |i|
curve[i] = (curve[i - i % step] * (step - i % step) + curve[i - i % step + step] * (i % step)) / step;
You may be tempted to just do these calculations "on the fly" while decoding the pixel data, however based on how many pixels there are to decode it is very likely more efficient to calculate the entire curve in advance. Especially for parallel decoding of multiple images.
Raw Pixel Data IFD
You can identify the IFD holding the raw pixel data with the following tags:
0x106"PhotometricInterpretation": Should be32803"CFA".0x103"Compression": Should be34713"NEF".Do not confuse this with tag
0x0093"NEF Compression" of the makernote!Some very old Nikon cameras support generating uncompressed NEF files. I have yet to look into those, however I suspect they either set this direntry to
0x1"None" and/or set0x0093"NEF Compression" in the makernote to0x2"Uncompressed".
These other tags are of interest:
0x101"Image Width": Width of the image in pixel.0x100"Image Height": Height of the image in pixel.0x111"StripOffsets": Offset to the raw pixel data.Note that TIFF supports this to hold multiple values, so that assembling an image could require parsing multiple data strips. However NEF does not seem to make use of this feature, luckily.
Probably still a good idea to assert that this direntry holds only one offset!
0x117"StripByteCounts": Byte count of the raw pixel data.0x102"BitsPerSample": Sample depth of the raw pixel data.Another direntry were TIFF supports multiple values, but NEF only uses a single one.
Should be either 12 or 14.
According to one source↗, the raw pixels may be stored as a format called "JPEG lossless". There are multiple things called "JPEG lossless" or "lossless JPEG" or variations thereof, but this likely refers to the lossless compression scheme that was part of the very first JPEG specification. I am not currently willing to pay money to read the JPEG spec, so I am unable to confirm whether this actually holds true.
Either way, the raw pixel data is compressed twice. Once with the linearisation table, and another time with a huffman-like encoding.
Decoding the raw pixel data
Work in progress...